package com.JUC.cha04.sec01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 可重入锁
 * 指的是可重复递归调用的锁，在外层使用锁之后在内层仍然可以使用，并且不会发生死锁，这样的锁就叫做可重入锁
 * 简单来说就是：在一个 synchronized 修饰的方法或者代码块的内部调用本类的其他 synchronized 修饰的方法或代码块时，时永远可以得到锁的
 *
 *
 * 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
 * 当执行monitorenter时，比如目标锁对象的计数器为零，那么说明它没有被其他线程所持有，Java虚拟机会将该锁对象的持有线程设置为当前线程，并且将其计数器加1
 *
 * 在目标锁对象的计数器不为零的情况下，如果锁对象的持有线程是当前线程，那么Java虚拟机可以将其计数器加1，否则需要等待，直至持有线程释放该锁。
 *
 * 当执行monitorexit时，Java虚拟机则需要将锁对象的计数器减1.计数器为0代表锁已经释放
 */
public class ReEntryLockDemo {

    public synchronized void m1(){
        System.out.println(Thread.currentThread().getName() + "\tcome in m1");
        m2();
        System.out.println("end m1");
    }
    public synchronized void m2(){
        System.out.println(Thread.currentThread().getName() + "\tcome in m2");
        m3();
        System.out.println("end m2");
    }
    public synchronized void m3(){
        System.out.println(Thread.currentThread().getName() + "\tcome in m3");
        System.out.println("end m3");
    }

    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        /*ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
        new Thread(() -> {
            reEntryLockDemo.m1();
        }, "t2").start();*/


        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t外层lock");
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "\t内层lock");
                } finally {
                    lock.unlock();
                    // 正常情况下加锁几次就要释放锁几次，否则第二个线程始终拿不到锁，导致一直在等待
                }
            } finally {
                lock.unlock();
            }
        }, "t1").start();

        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t外层lock");
            } finally {
                lock.unlock();
            }
        }, "t2").start();

    }

    private static void reEntryM1() {
        Object o = new Object();

        new Thread(() -> {
            synchronized (o){
                System.out.println("外部锁");
                synchronized (o){
                    System.out.println("中部锁");
                    synchronized (o){
                        System.out.println("内部锁");
                    }
                }
            }
        }, "t1").start();
    }


}
